﻿using System;
using System.Diagnostics;
using System.Collections.Generic;
using System.Text;
using System.Data.SqlClient;
using System.Data.SqlTypes;

using Noris.Schedule.Support;
using Noris.Schedule.Support.Data;
using Noris.Schedule.Support.Sql;
using Noris.Schedule.Support.Core;
using Noris.Schedule.Support.DataFace;

using Noris.Schedule.Planning.DataFace;

// Tento soubor obsahuje třídy, které řeší částečné (poměrné) využívání výrobních kapacit
namespace Noris.Schedule.Planning.ProcessData
{
	#region CLASS CapacityUseRatioMapCls : správce disponibilní kapacity v rámci jednoho Desk
	/// <summary>
	/// CapacityUseRatioMapCls : správce disponibilní kapacity v rámci jednoho Desk.
	/// Zajišťuje evidenci čerpání kapacity jednotlivými položkami WorkID, 
	/// přičemž čerpání kapacity je dáno počátkem, koncem a množstvím spotřebované kapacity (poměr větší než 0 a menší nebo rovno 1).
	/// Správce umožňuje vyhledat volný čas pro požadovaný čas a směr, a požadovanou kapacitu.
	/// Při vyhledání volného času je možno specifikovat položky, které jsou pro tu chvíli neviditelné.
	/// </summary>
	internal class CapacityUseRatioMapCls
	{
		#region KONSTRUKCE, PROMĚNNÉ, OVERRIDES
		/// <summary>
		/// Vytvoří a vrátí objekt pro daný kapacitní stůl.
		/// Z něja načte večekrá data pro svůj provoz.
		/// </summary>
		/// <param name="capacityDesk"></param>
		/// <returns></returns>
		internal static CapacityUseRatioMapCls CreateForDesk(CapacityDeskCls capacityDesk)
		{
			if (capacityDesk == null) return null;
			if (capacityDesk.CapacityLevel == null) return null;
			TimeRange timeRange = capacityDesk.Time;
			if (timeRange.IsEmpty || timeRange.Seconds <= 0D) return null;

			CapacityUseRatioMapCls map = new CapacityUseRatioMapCls(capacityDesk, timeRange);
			return map;
		}
		/// <summary>
		/// Vytvoří a vrátí objekt pro testovací účely. Nemá vztah na žádného parenta.
		/// </summary>
		/// <param name="timeRange"></param>
		/// <returns></returns>
		internal static CapacityUseRatioMapCls CreateForTest(TimeRange timeRange)
		{
			CapacityUseRatioMapCls map = new CapacityUseRatioMapCls(null, timeRange);
			return map;
		}
		private CapacityUseRatioMapCls(CapacityDeskCls capacityDesk, TimeRange timeRange)
		{
			this.CapacityDesk = capacityDesk;
			this.TimeRange = timeRange;
			this._Init();
		}
		private void _Init()
		{
			this._ChangeList = new List<CapacityUseRatioItem>();
			this._ChangeList.Add(new CapacityUseRatioItem(0, TimeRange.Begin, 1M));
			this._ChangeList.Add(new CapacityUseRatioItem(0, TimeRange.End, -1M));
			this._RecalcItems();
		}
		/// <summary>
		/// Soupis jednotlivých změn ve využití této kapacity.
		/// </summary>
		private List<CapacityUseRatioItem> _ChangeList;
		/// <summary>
		/// ID sady interWork, pro kterou je připraven seznam this._ChangeList.
		/// Sada interWork obsahuje seznam WorkID, které se nemají počítat do spotřebované kapacity (jde o položky, které jsou dočasně potlačeny, nejsou viditelné).
		/// Při hledáné volné kapacity se tyto položky ignorují. Více viz metoda this._PrepareInterWork().
		/// </summary>
		private int _PrepareForInterWorkID;
		/// <summary>
		/// Příznak, že data v seznamu this._ChangeList jsou reálně korigována pro potřeby sady změn interWork.
		/// Důležité je slovo "reálně": pokud sada interWork popisuje položky WorkID, které nejsou přítomny v našem seznamu this._ChangeList,
		/// pak tento seznam nebude nijak modifikován, a přitom objekt je pro daný seznam interWork připraven 
		/// (nemusí se znovu připravovat při dalším požadavku se shodným seznamem interWork).
		/// Více viz metoda this._PrepareInterWork().
		/// </summary>
		private bool _PrepareForInterWork;
		#endregion
		#region PUBLIC PROPERTY
		/// <summary>
		/// Celkový čas v tomto prvku. Je neprázdný, kladný.
		/// </summary>
		internal TimeRange TimeRange { get; private set; }
		/// <summary>
		/// Data kapacitního stolu (Desk)
		/// </summary>
		internal CapacityDeskCls CapacityDesk { get; private set; }
		/// <summary>
		/// Způsob plánování = omezení kapacit, typově přesné.
		/// Pouze hodnota Limited anebo Unlimited.
		/// </summary>
		internal LimitedCType UnitLimitedCType { get { return this.CapacityLevel.UnitLimitedCType; } }
		/// <summary>
		/// Příznak existence objektu Data kapacitního stolu (Desk)
		/// </summary>
		internal bool HasCapacityDesk { get { return (this.CapacityDesk != null); } }
		/// <summary>
		/// Data stavu kapacit
		/// </summary>
		internal CapacityLevelItemCls CapacityLevel { get { return (HasCapacityDesk ? this.CapacityDesk.CapacityLevel : null); } }
		/// <summary>
		/// Kapacitní plánovací jednotka = konkrétní výrobní prostředek
		/// </summary>
		internal int PlanUnitC { get { return (HasCapacityDesk ? this.CapacityLevel.PlanUnitC : 0); } }
		/// <summary>
		/// Index stolu kapacitní jednotky = kde konkrétně se práce koná
		/// </summary>
		internal int CapacityDeskIndex { get { return (HasCapacityDesk ? this.CapacityDesk.DeskIndex : 0); } }
		/// <summary>
		/// Klíč stavu kapacit = pro rychlé odebrání při odregistraci
		/// </summary>
		internal int CapacityLevelId { get { return (HasCapacityDesk ? this.CapacityLevel.LevelID : 0); } }
		#endregion
		#region PUBLIC METODY A PROPERTY
		/// <summary>
		/// Vyhledá volný prostor v soupisu kapacit pro daný čas, 
		/// směr a požadované množství kapacity (poměr useRatio = větší než 0 a menší nebo roven 1)
		/// Vrací informaci o volném čase.
		/// Pokud nenajde žádný vyhovující čas, vrací null.
		/// Může vyhodit chybu, pokud je typ kapacit jiný než Limited nebo Unlimited,
		/// a pokud je směr časového vektoru jiný než ToHistory nebo ToFuture.
		/// </summary>
		/// <param name="findArgs">Parametry hledání volného času</param>
		/// <param name="findFreeRatio">Poměrná část kapacity, kterou hledáme</param>
		/// <remarks>
		/// findArgs.InterWork = Soupis položek WorkID, které jsou nyní při tomto hledání neviditelné. 
		/// Pokud je předáno null, jsou všechny položky viditelné.
		/// Pokud je dvakrát po sobě předán stejný objekt, pak při druhém a dalším volání je jeho zpracování rychlejší než při prvním volání (kdy probíhá příprava objektu this na data v interWork).
		/// Pokud je požadováno hledání v neomezených kapacitách, je tento objekt ignorován (logicky: nemá význam).
		/// </remarks>
		/// <returns></returns>
		internal CapacityUseRatioFreeTime FindFreeTime(CapacityFindFreeTimeArgs findArgs, decimal findFreeRatio)
		{
			CapacityUseRatioFreeTime result = null;
			switch (findArgs.GetCurrentLimitedCType(this))
			{
				case LimitedCType.Limited:
					result = this._FindFreeTimeLimited(findArgs, findFreeRatio);
					break;
				case LimitedCType.Unlimited:
					result = this._FindFreeTimeUnLimited(findArgs, findFreeRatio);
					break;
			}
			return result;
		}
		/// <summary>
		/// Zapíše využití kapacity pro daný čas a hodnotu
		/// </summary>
		/// <param name="workID">ID jednotky práce (WorkUnit)</param>
		/// <param name="timeRange">Čas, po který je kapacita využívána</param>
		/// <param name="useRatio">Poměr využití kapacity</param>
		internal void SaveUsedTime(int workID, TimeRange timeRange, decimal useRatio)
		{
			DateTime begin = _GetDateIntoRange(timeRange.Begin, this.TimeRange);
			DateTime end = _GetDateIntoRange(timeRange.End, this.TimeRange);
			if (end <= begin) return;                                        // Záporný nebo nulový čas => konec.

			int indexEnd = this._FindIndex(new TimeVector(end, TimeRange.TimeDirection.ToFuture));
			this._ChangeList.Insert(indexEnd, new CapacityUseRatioItem(workID, end, useRatio));

			int indexBegin = this._FindIndex(new TimeVector(begin, TimeRange.TimeDirection.ToHistory));
			this._ChangeList.Insert(indexBegin + 1, new CapacityUseRatioItem(workID, begin, -useRatio));

			this._RecalcItems(indexBegin, indexEnd + 1);
		}
		/// <summary>
		/// Odebere daný WorkID ze seznamu položek a z odčerpané kapacity
		/// </summary>
		/// <param name="workID">ID jednotky práce (WorkUnit)</param>
		internal void RemoveWork(int workID)
		{
			if (workID == 0) return;

			int indexBegin = -1;
			int indexEnd = -1;
			for (int index = 0; index < this._ChangeList.Count; index++)
			{
				if (this._ChangeList[index].WorkID != workID) continue;
				if (indexBegin < 0) indexBegin = index - 1;              // Index prvního prvku k přepočtu = index prvku před tím, který budu první odebírat
				this._ChangeList.RemoveAt(index);                        // Odeberu záznam na tomto indexu
				indexEnd = index;                                        // Index posledního prvku k přepočtu = index prvku za tím, který budu poslední odebírat
				index--;                                                 // Pokračovat od dalšího prvku, který nyní propadl o 1 pozici k nule.
			}
			if (indexBegin >= 0)
				this._RecalcItems(indexBegin, indexEnd);
		}
		/// <summary>
		/// Metoda zjistí, zda v daném časovém úseku (definován v parametru conflictTime) existují konflikty s položkami seznamu interWork.
		/// </summary>
		/// <param name="conflictTime"></param>
		/// <param name="interWork"></param>
		/// <returns></returns>
		internal void SetConflictState(TimeRange conflictTime, CapacityInterWorkCls interWork)
		{
			// a) pokud na vstupu není žádný interWork, nebo je prázdný, nebo čas je prázdný, vracím null:
			if (interWork == null || interWork.IsEmpty || interWork.InterWorkID == 0 || conflictTime.IsEmpty || conflictTime.Seconds <= 0D)
				return;

			// b) pokud nejsem připraven na daný seznam interWork, jde o chybu:
			if (this._PrepareForInterWorkID != interWork.InterWorkID)
				Throw.SysError(MessageInfo.Get("Při určování konfliktu volného času je předán jiný objekt InterWork (%0), než pro který byl vyhledán volný čas (%1)", interWork.InterWorkID, this._PrepareForInterWorkID));

			// c) pokud jsme tedy na objekt interWork připraveni, ale prakticky se nás netýká, můžeme skončit:
			if (!this._PrepareForInterWork)
				return;

			// d) projdu všechny svoje změny které jsou skryté, a sestavím z nich seznam TimeRange:
			Dictionary<int, TimeRange> invisibleItems = new Dictionary<int, TimeRange>();
			foreach (CapacityUseRatioItem item in this._ChangeList)
			{
				if (!item.InvisibleItem) continue;            // Zajímají mě jen neviditelné položky = ty, které jsou skryté kvůli konfliktu.
				if (!invisibleItems.ContainsKey(item.WorkID))
					invisibleItems.Add(item.WorkID, TimeRange.Empty);

				// Za změnu zápornou nastavím datum počátku (na počátku je mínus), za změnu kladnou nastavím datum konce.
				// Nemohu jednoduše měnit hodnotu Begin a End přímo v Dictionary, protože TimeRange je struktura,
				//   a Dictionary mi v indexeru vrátí kopii struktury, a ne referenci na objekt. 
				// Mohu ale vložit novou strukturu:
				if (item.ChangeRatio < 0M)
					invisibleItems[item.WorkID] = new TimeRange(item.ChangeDate, invisibleItems[item.WorkID].End);
				else if (item.ChangeRatio > 0M)
					invisibleItems[item.WorkID] = new TimeRange(invisibleItems[item.WorkID].Begin, item.ChangeDate);
			}

			// e) projdu neviditelné items, určím zda jsou v konfliktu, a pro konfliktní položky uložím stav konfliktu:
			foreach (var change in invisibleItems)
			{
				if (conflictTime.IsConjunct(change.Value))
					interWork.SetConflictState(change.Key);
			}
		}
		#endregion
		#region PRIVÁTNÍ METODY : HLEDÁNÍ VOLNÉHO ČASU, OMEZENÉ KAPACITY
		/// <summary>
		/// Vyhledá volný prostor v soupisu kapacit pro daný čas,
		/// směr a požadované množství kapacity (poměr useRatio = větší než 0 a menší nebo roven 1).
		/// Aceptuje obsazenost kapacit (plánuje do omezených kapacit).
		/// Vrací informaci o volném čase.
		/// Může vyhodit chybu, pokud je typ směr časového vektoru jiný než ToHistory nebo ToFuture.
		/// </summary>
		/// <param name="findArgs">Parametry hledání volného času</param>
		/// <param name="findFreeRatio">Poměrná část kapacity, kterou hledáme</param>
		/// <returns></returns>
		private CapacityUseRatioFreeTime _FindFreeTimeLimited(CapacityFindFreeTimeArgs findArgs, decimal findFreeRatio)
		{
			this._PrepareInterWork(findArgs.InterWork);
			switch (findArgs.TimeVector.Direction)
			{
				case TimeRange.TimeDirection.ToFuture:
					return _FindFreeTimeLimitedToFuture(findArgs.TimeVector.Date, findFreeRatio);
				case TimeRange.TimeDirection.ToHistory:
					return _FindFreeTimeLimitedToHistory(findArgs.TimeVector.Date, findFreeRatio);
				default:
					// chyba:
					Throw.SysError(MessageInfo.Get("Chyba: směr času pro hledání volného času smí být pouze ToFuture nebo ToHistory."));
					break;
			}
			return null;
		}
		/// <summary>
		/// Vyhledá volný prostor v soupisu kapacit pro daný čas, směr ToFuture
		/// a požadované množství kapacity (poměr useRatio = větší než 0 a menší nebo roven 1)
		/// </summary>
		/// <param name="startTime">Výchozí čas hledání</param>
		/// <param name="findFreeRatio">Poměrná část kapacity, kterou hledáme</param>
		/// <returns></returns>
		private CapacityUseRatioFreeTime _FindFreeTimeLimitedToFuture(DateTime startTime, decimal findFreeRatio)
		{
			int lastIndex = this._ChangeList.Count - 1;
			// Pokud hledáme čas před (neb v) našem začátku, a směrem ToHistory, pak nelze najít nic vhodného:
			if (startTime >= this._ChangeList[lastIndex].ChangeDate) return null;     // Prvek [lastIndex] existuje vždy.
			//  ... nyní je jisté, že prvek [lastIndex] má datum ChangeDate > startTime.

			// Najdu poslední prvek, který je vlevo před hledaným časem (to je prvek, za který bych případně vložil začátek svého času):
			DateTime beginDate = startTime;
			int index = this._FindIndex(new TimeVector(startTime, TimeRange.TimeDirection.ToHistory), true);

			// Prohledám prvky za výchozím prvkem, a dohledám pár prvků, mezi nimiž je nenulový volný čas a dostatečná kapacita:
			int beginIndex = -1;
			int currIndex = index;
			while (currIndex >= 0)
			{
				CapacityUseRatioItem currItem = this._ChangeList[currIndex];  // Prvek na průběžném indexu, počínaje zadaným indexem
				if (!currItem.InvisibleItem && currItem.ResultingFreeRatio >= findFreeRatio)
				{	// Prvek musí být viditelný, a na jeho konci musí být dostatečená kapacita, pak se jím teprve budu zabývat:
					DateTime currDate = (startTime > currItem.ChangeDate ? startTime : currItem.ChangeDate);  // Aktuální datum: mám použít StartTime? (když je větší než datum prvku current)
					int nextIndex = this._FindNextVisibleIndex(currIndex);    // najdu index následující viditelné položky
					if (nextIndex < 0) break;                                 // Vpravo už není nic viditelného? Skončím.
					CapacityUseRatioItem nextItem = this._ChangeList[nextIndex];      // Prvek o 1 doprava
					DateTime nextDate = nextItem.ChangeDate;
					if (nextDate > currDate)
					{	// Pokud datum na prvku na příštím indexu je větší než aktuální, mám správnou dvojici.
						beginDate = currDate;
						beginIndex = currIndex;
						currIndex = nextIndex;
						break;
					}
					currIndex = nextIndex;
				}
				else
				{	// Prvek curr mi nevyhovuje, jdu na další:
					currIndex = this._FindNextVisibleIndex(currIndex);    // najdu index následující viditelné položky
				}
			}
			// Pokud jsem nenašel počáteční prvek, značí to že není žádný volný čas, a skončím:
			if (beginIndex < 0) return null;

			// Nyní mám počáteční index (beginIndex), a mám i nastaven currIndex = index viditelné položky za položkou beginIndex.
			// Mezi nimi je nenulový čas.
			// Ale ono může být času daleko více: dohledám tedy prvek endIndex, na kterém skutečně končí volný čas pro potřebnou kapacitu:
			// Prvek endIndex může být shodný s currIndex, ale může být i o něco více vpravo:
			int endIndex = -1;
			while (currIndex >= 0)
			{
				CapacityUseRatioItem nextItem = this._ChangeList[currIndex];
				if (!nextItem.InvisibleItem)
				{	// Pokud prvek next je viditelný:
					if (nextItem.InitialFreeRatio < findFreeRatio)
						// a pokud má menší kapacitu než potřebuji, pak jej nechci a končím hledání:
						break;
					// Prvek je viditelný, a má dostatečnou kapacitu:
					//  pak ten příští prvek akceptuji jakožto potenciální end prvek:
					//  a nedá mi to a jdu zkusit hledat ještě další (doprava):
					endIndex = currIndex;
				}
				currIndex = this._FindNextVisibleIndex(currIndex);    // najdu index předchozí viditelné položky
			}
			if (endIndex < 0) return null;

			// Nyní bych měl mít endIndex >= 0 (to je dané první smyčkou), takže sestavím info o volném čase:
			CapacityUseRatioFreeTime result = new CapacityUseRatioFreeTime(this.CapacityDesk, beginIndex, beginDate, endIndex, this._ChangeList[endIndex].ChangeDate, findFreeRatio, TimeRange.TimeDirection.ToFuture, LimitedCType.Limited);
			return result;

		}
		/// <summary>
		/// Vyhledá volný prostor v soupisu kapacit pro daný čas, směr ToHistory 
		/// a požadované množství kapacity (poměr useRatio = větší než 0 a menší nebo roven 1)
		/// </summary>
		/// <param name="startTime">Výchozí čas hledání</param>
		/// <param name="findFreeRatio">Poměrná část kapacity, kterou hledáme</param>
		/// <returns></returns>
		private CapacityUseRatioFreeTime _FindFreeTimeLimitedToHistory(DateTime startTime, decimal findFreeRatio)
		{
			// Pokud hledáme čas před (neb v) našem začátku, a směrem ToHistory, pak nelze najít nic vhodného:
			if (startTime <= this.TimeRange.Begin) return null;
			//  ... nyní je jisté, že hledáme FreeTime pro čas > začátek tohoto soupisu

			// Najdu první prvek, který je vpravo za hledaným časem (to je prvek, před který bych případně vložil konec svého času):
			DateTime endDate = startTime;
			int index = this._FindIndex(new TimeVector(startTime, TimeRange.TimeDirection.ToFuture), true);

			// Prohledám prvky před výchozím koncovým prvkem, a dohledám pár prvků, mezi nimiž je nenulový volný čas a dostatečná kapacita:
			int endIndex = -1;
			int currIndex = index;
			while (currIndex > 0)
			{
				CapacityUseRatioItem currItem = this._ChangeList[currIndex];  // Prvek na průběžném indexu, počínaje zadaným indexem
				if (!currItem.InvisibleItem && currItem.InitialFreeRatio >= findFreeRatio)
				{	// Prvek musí být viditelný, a na jeho začátku musí být dostatečená kapacita, pak se jím teprve budu zabývat:
					DateTime currDate = (startTime < currItem.ChangeDate ? startTime : currItem.ChangeDate);  // Aktuální datum: mám použít StartTime? (když je menší než datum prvku current)
					int prevIndex = this._FindPrevVisibleIndex(currIndex);    // najdu index předchozí viditelné položky
					if (prevIndex < 0) break;                                 // Vlevo už není nic viditelného? Skončím.
					CapacityUseRatioItem prevItem = this._ChangeList[prevIndex];
					DateTime prevDate = prevItem.ChangeDate;
					if (prevDate < currDate)
					{	// Pokud datum na prvku na předešlém indexu je menší než aktuální, mám správnou dvojici.
						// Na indexu (i) je prvek, který má na svém začátku dostatečnou kapacitu, a před nímž (od prvku na indexu i-1) je čas kladný, a větší než minimální:
						endDate = currDate;
						endIndex = currIndex;
						currIndex = prevIndex;
						break;
					}
					currIndex = prevIndex;
				}
				else
				{	// Prvek curr mi nevyhovuje, jdu na další:
					currIndex = this._FindPrevVisibleIndex(currIndex);    // najdu index předchozí viditelné položky
				}
			}
			// Pokud jsem nenašel koncový prvek, značí to že není žádný volný čas, a skončím:
			if (endIndex < 0) return null;

			// Nyní mám koncový index (endIndex), a mám i nastaven currIndex = index viditelné položky před položkou endIndex.
			// Mezi nimi je nenulový čas.
			// Ale ono může být času daleko více: dohledám tedy prvek beginIndex, na kterém skutečně začíná volný čas pro potřebnou kapacitu:
			// Prvek beginIndex může být shodný s currIndex, ale může být i o něco více vlevo:
			int beginIndex = -1;
			while (currIndex >= 0)
			{
				CapacityUseRatioItem prevItem = this._ChangeList[currIndex];
				if (!prevItem.InvisibleItem)
				{	// Pokud prvek prev je viditelný:
					if (prevItem.ResultingFreeRatio < findFreeRatio)
						// a pokud má menší kapacitu než potřebuji, pak jej nechci a končím hledání:
						break;
					// Prvek je viditelný, a má dostatečnou kapacitu:
					//  pak ten předchozí prvek akceptuji jakožto potenciální begin prvek:
					//  a nedá mi to a jdu zkusit hledat ještě další (doleva):
					beginIndex = currIndex;
				}
				currIndex = this._FindPrevVisibleIndex(currIndex);    // najdu index předchozí viditelné položky
			}
			if (beginIndex < 0) return null;

			// Nyní bych měl mít beginIndex >= 0 (to je dané přinejmenším první smyčkou), takže sestavím info o volném čase:
			CapacityUseRatioFreeTime result = new CapacityUseRatioFreeTime(this.CapacityDesk, beginIndex, this._ChangeList[beginIndex].ChangeDate, endIndex, endDate, findFreeRatio, TimeRange.TimeDirection.ToHistory, LimitedCType.Limited);
			return result;
		}
		/// <summary>
		/// Připraví svá data na práci s daným seznamem neviditelných WorkID.
		/// Tzn. pokud je předaný nějaký seznam položek WorkID, zajistí, že tyto položky budou dočasně ukryté 
		/// Seznam obsahuje informace o položkách WorkID, které máme při hledání volného času ignorovat.
		/// Seznam může být null, pak pracujeme v režimu "Všechno vidím".
		/// Seznam může mít stejné ID, pro jaké už je tento objekt (this) připraven, pak se nemusíme na nic připravovat.
		/// </summary>
		/// <param name="interWork">Soupis položek WorkID, které jsou nyní při tomto hledání neviditelné. Pokud je předáno null, jsou všechny položky viditelné.</param>
		private void _PrepareInterWork(CapacityInterWorkCls interWork)
		{
			int interId = ((interWork == null) ? 0 : interWork.InterWorkID);
			if (interId == this._PrepareForInterWorkID) return;             // Pro tento seznam máme naše data připravena.

			// Provedeme přípravu:
			if (interId == 0)
			{	// Zrušení dat, připravených pro seznam interWork:
				if (this._PrepareForInterWork)
				{	// Měli jsme nějaké položky, které byly skutečně modifikovány?
					bool changed = this._PrepareInterWorkClear();
					if (changed)
						this._RecalcItems();
					this._PrepareForInterWork = false;
				}
			}
			else
			{	// Připravíme náš seznam this._ChangeList tak, aby odpovídal danému seznamu (seznam není null, jinak bychom nebyli v této větvi):
				this._PrepareForInterWork = this._PrepareInterWorkSet(interWork);
				if (this._PrepareForInterWork)
					this._RecalcItems();
			}
			this._PrepareForInterWorkID = interId;
		}
		/// <summary>
		/// Projde naše položky v seznamu this._ChangeList, a do všech položek nastaví jejich viditelnost 
		/// podle informací ze seznamu interWork.
		/// Vrací příznak true, když se na některé položce skutečně změnila její viditelnost (pak volající metoda zajistí rekalkulaci).
		/// </summary>
		/// <param name="interWork">Soupis položek WorkID, které jsou nyní při tomto hledání neviditelné. Nesmí být null.</param>
		/// <returns></returns>
		private bool _PrepareInterWorkSet(CapacityInterWorkCls interWork)
		{
			// Pokud je na vstupu soupis, který není finalizovaný, pak jej odmítnu s chybou:
			if (!interWork.IsFinalized)
				Throw.SysError(MessageInfo.Get("Chyba: pro hledání volného času nelze použít objekt CapacityInterWorkCls, který není finalizovaný."));

			bool result = false;
			foreach (CapacityUseRatioItem item in this._ChangeList)
			{
				bool isInvisible = interWork.ContainWorkID(item.WorkID);
				if (isInvisible != item.InvisibleItem)
				{
					item.InvisibleItem = isInvisible;
					if (!result) result = true;
				}
			}
			return result;
		}
		/// <summary>
		/// Projde naše položky v seznamu this._ChangeList, a do všech položek nastaví jejich viditelnost = true 
		/// (nastaví InvisibleItem = false).
		/// </summary>
		private bool _PrepareInterWorkClear()
		{
			bool result = false;
			foreach (CapacityUseRatioItem item in this._ChangeList)
			{
				if (item.InvisibleItem)
				{
					item.InvisibleItem = false;
					if (!result) result = true;
				}
			}
			return result;
		}
		#endregion
		#region PRIVÁTNÍ METODY : HLEDÁNÍ VOLNÉHO ČASU, NEOMEZENÉ KAPACITY
		/// <summary>
		/// Vyhledá volný prostor v soupisu kapacit pro daný čas, 
		/// směr a požadované množství kapacity (poměr useRatio = větší než 0 a menší nebo roven 1).
		/// Plánuje do neomezených kapacit. Aktuální obsazenost ignoruje.
		/// Vrací informaci o volném čase.
		/// Může vyhodit chybu, pokud je typ směr časového vektoru jiný než ToHistory nebo ToFuture.
		/// </summary>
		/// <param name="findArgs">Parametry hledání volného času</param>
		/// <param name="findFreeRatio">Poměrná část kapacity, kterou hledáme</param>
		/// <returns></returns>
		private CapacityUseRatioFreeTime _FindFreeTimeUnLimited(CapacityFindFreeTimeArgs findArgs, decimal findFreeRatio)
		{
			switch (findArgs.TimeVector.Direction)
			{
				case TimeRange.TimeDirection.ToFuture:
					return _FindFreeTimeUnLimitedToFuture(findArgs.TimeVector.Date, findFreeRatio);
				case TimeRange.TimeDirection.ToHistory:
					return _FindFreeTimeUnLimitedToHistory(findArgs.TimeVector.Date, findFreeRatio);
				default:
					// chyba
					Throw.SysError(MessageInfo.Get("Chyba: směr času pro hledání volného času smí být pouze ToFuture nebo ToHistory."));
					break;
			}
			return null;
		}
		/// <summary>
		/// Metoda najde a vrátí volný čas ve směru ToFuture, bez omezení kapacit
		/// </summary>
		/// <param name="startTime">Výchozí čas hledání</param>
		/// <param name="findFreeRatio">Poměrná část kapacity, kterou hledáme</param>
		/// <returns></returns>
		private CapacityUseRatioFreeTime _FindFreeTimeUnLimitedToFuture(DateTime startTime, decimal findFreeRatio)
		{
			if (startTime >= this.TimeRange.End) return null;
			int beginIndex = this._FindIndex(new TimeVector(startTime, TimeRange.TimeDirection.ToHistory));
			int endIndex = this._FindIndex(new TimeVector(this.TimeRange.End, TimeRange.TimeDirection.ToFuture));
			DateTime beginTime = (startTime < this.TimeRange.Begin ? this.TimeRange.Begin : startTime);
			CapacityUseRatioFreeTime result = new CapacityUseRatioFreeTime(this.CapacityDesk, beginIndex, beginTime, endIndex, this.TimeRange.End, findFreeRatio, TimeRange.TimeDirection.ToFuture, LimitedCType.Unlimited);
			return result;
		}
		/// <summary>
		/// Metoda najde a vrátí volný čas ve směru ToHistory, bez omezení kapacit
		/// </summary>
		/// <param name="startTime">Výchozí čas hledání</param>
		/// <param name="findFreeRatio">Poměrná část kapacity, kterou hledáme</param>
		/// <returns></returns>
		private CapacityUseRatioFreeTime _FindFreeTimeUnLimitedToHistory(DateTime startTime, decimal findFreeRatio)
		{
			if (startTime <= this.TimeRange.Begin) return null;
			int beginIndex = this._FindIndex(new TimeVector(this.TimeRange.Begin, TimeRange.TimeDirection.ToHistory));
			int endIndex = this._FindIndex(new TimeVector(startTime, TimeRange.TimeDirection.ToFuture));
			DateTime endTime = (startTime > this.TimeRange.End ? this.TimeRange.End : startTime);
			CapacityUseRatioFreeTime result = new CapacityUseRatioFreeTime(this.CapacityDesk, beginIndex, this.TimeRange.Begin, endIndex, endTime, findFreeRatio, TimeRange.TimeDirection.ToHistory, LimitedCType.Unlimited);
			return result;
		}
		#endregion
		#region PRIVÁTNÍ METODY : FIND INDEX, DATE, RECALC
		/// <summary>
		/// Najde index první viditelné položky doleva od daného indexu.
		/// Může vrátit -1, pokud je na vstupu 0.
		/// </summary>
		/// <param name="currIndex">Výchozí index, ten se netestuje, ten se nevrací, od něj se odpíchnu</param>
		/// <returns></returns>
		private int _FindPrevVisibleIndex(int currIndex)
		{
			int changeCount = this._ChangeList.Count;
			if (currIndex <= 0) return -1;                       // 0 na vstupu => vrací -1, protože před prvkem 0 nic není.
			// Výchozí index (může být == počtu položek, protože první co s ním udělám, je dekrementace):
			int index = (currIndex <= changeCount ? currIndex : changeCount);
			while (index > 0)
			{
				index--;
				if (!this._ChangeList[index].InvisibleItem) return index;         // Viditelná položka = výstupní hodnota.
			}
			return -1;
		}
		/// <summary>
		/// Najde index první viditelné položky doprava od daného indexu.
		/// Může vrátit -1, pokud je na vstupu index posledního prvku.
		/// </summary>
		/// <param name="currIndex">Výchozí index, ten se netestuje, ten se nevrací, od něj se odpíchnu</param>
		/// <returns></returns>
		private int _FindNextVisibleIndex(int currIndex)
		{
			int changeCount = this._ChangeList.Count;
			if (currIndex >= (changeCount - 1)) return -1;       // Pokud je na vstupu index poslední položky nebo více, vrací se -1, protože za posledním prvkem nic není.
			// Výchozí index (může být == -1, protože první co s ním udělám, je inkrementace):
			int index = (currIndex >= -1 ? currIndex : -1);
			while (index < (changeCount - 1))
			{
				index++;
				if (!this._ChangeList[index].InvisibleItem) return index;         // Viditelná položka = výstupní hodnota.
			}
			return -1;
		}
		/// <summary>
		/// Najde index záznamu, který odpovídá danému vektoru času (čas a směr).
		/// Najde a vrátí index prvního nalezeného záznamu v daném směru, který má datum shodné nebo vyšší než požadavek, v daném směru.
		/// </summary>
		/// <param name="timeVector">Časový vektor</param>
		/// <returns></returns>
		private int _FindIndex(TimeVector timeVector)
		{
			return this._FindIndex(timeVector, false);
		}
		/// <summary>
		/// Najde index záznamu, který odpovídá danému vektoru času (čas a směr).
		/// Najde a vrátí index prvního nalezeného záznamu v daném směru, který má datum shodné nebo vyšší než požadavek, v daném směru.
		/// </summary>
		/// <param name="timeVector">Časový vektor</param>
		/// <param name="onlyVisibleItem">Hledat pouze viditelné položky (true) / hledat všechny, bez ohledu na viditelnost (false, default).</param>
		/// <returns></returns>
		private int _FindIndex(TimeVector timeVector, bool onlyVisibleItem)
		{
			if (onlyVisibleItem)
			{	// Jen viditelné položky
				switch (timeVector.Direction)
				{
					case TimeRange.TimeDirection.ToFuture:
						if (timeVector.Date >= this.TimeRange.End)
							// Pokud hledám ToFuture, a hledám datum větší než nebo shodné jako je můj konec, pak najdu první index záznamu s datem == konec, první z několika:
							//   (tedy: najdu index prvního záznamu s datem == konec, kt menším, a přidám 1 => tím najdu (od konce) první záznam s datem konce):
							return this._ChangeList.FindIndex(item => item.ChangeDate == this.TimeRange.End && !item.InvisibleItem);
						// Hledám ToFuture, pro datum < můj konec: hledám první záznam, jehož datum >= hledané datum:
						return this._ChangeList.FindIndex(item => item.ChangeDate >= timeVector.Date && !item.InvisibleItem);

					case TimeRange.TimeDirection.ToHistory:
						if (timeVector.Date <= this.TimeRange.Begin)
							// Pokud hledám ToHistory, a hledám datum menší než nebo shodné jako je můj začátek, pak najdu poslední index záznamu s datem == počátek, poslední z několika:
							return this._ChangeList.FindLastIndex(item => item.ChangeDate == this.TimeRange.Begin && !item.InvisibleItem);
						// Hledám ToHistory, pro datum > můj počátek: hledám poslední záznam, jehož datum <= hledané datum:
						return this._ChangeList.FindLastIndex(item => item.ChangeDate <= timeVector.Date && !item.InvisibleItem);
				}
			}
			else
			{	// Bez ohledu na viditelnost
				switch (timeVector.Direction)
				{
					case TimeRange.TimeDirection.ToFuture:
						if (timeVector.Date >= this.TimeRange.End)
							// Pokud hledám ToFuture, a datum větší než nebo shodné jako je můj konec, pak najdu index záznamu s datem == konec, první z několika:
							//   (tedy: najdu index posledního záznamu s datem menším, a přidám 1 => tím najdu (od konce) první záznam s datem konce):
							return this._ChangeList.FindLastIndex(item => item.ChangeDate < this.TimeRange.End) + 1;
						// Hledám ToFuture, pro datum < můj konec: hledám první záznam, jehož datum >= hledané datum:
						return this._ChangeList.FindIndex(item => item.ChangeDate >= timeVector.Date);
					case TimeRange.TimeDirection.ToHistory:
						if (timeVector.Date <= this.TimeRange.Begin)
							// Pokud hledám ToHistory, a datum menší než nebo shodné jako je můj začátek, pak najdu index záznamu s datem == počátek, poslední z několika:
							//   (tedy: najdu index prvního záznamu s datem větším, a odečtu 1 => tím najdu (od začátku) poslední záznam s datem počátku):
							return this._ChangeList.FindIndex(item => item.ChangeDate > this.TimeRange.Begin) - 1;
						// Hledám ToHistory, pro datum > můj počátek: hledám poslední záznam, jehož datum <= hledané datum:
						return this._ChangeList.FindLastIndex(item => item.ChangeDate <= timeVector.Date);
				}
			}
			return -1;
		}
		/// <summary>
		/// Vrátí dané datum tak, aby bylo v rozmezí daného TimeRange.
		/// </summary>
		/// <param name="date"></param>
		/// <param name="timeRange"></param>
		/// <returns></returns>
		private DateTime _GetDateIntoRange(DateTime date, TimeRange timeRange)
		{
			if (timeRange.Begin > timeRange.End) return DateTime.MinValue;
			if (date < timeRange.Begin) return timeRange.Begin;
			if (date > timeRange.End) return timeRange.End;
			return date;
		}
		/// <summary>
		/// Přepočet volného času na položkách
		/// </summary>
		private void _RecalcItems()
		{
			decimal transientFreeRatio = 0M;
			foreach (CapacityUseRatioItem item in this._ChangeList)
				item.RecalcItemFree(ref transientFreeRatio);
		}
		/// <summary>
		/// Přepočet volného času na položkách
		/// </summary>
		/// <param name="indexBegin">Index první položky, která bude přepočtena</param>
		/// <param name="indexEnd">Index poslední položky, která bude přepočtena</param>
		private void _RecalcItems(int indexBegin, int indexEnd)
		{
			// Výchozí stav (tj. stav na začátku první přepočítávané položky) je roven koncovému stavu na předelšé položce, pro index 0 se bere stav 0M:
			decimal transientFreeRatio = (indexBegin == 0 ? 0M : this._ChangeList[indexBegin - 1].ResultingFreeRatio);
			for (int index = indexBegin; index <= indexEnd; index++)
				this._ChangeList[index].RecalcItemFree(ref transientFreeRatio);
		}
		#endregion
	}
	#endregion
	#region CLASS CapacityUseRatioItem : Záznam o změně množství disponibilní kapacity v rámci seznamu v CapacityDeskFloatCls
	/// <summary>
	/// Záznam o změně množství disponibilní kapacity v rámci seznamu v CapacityDeskFloatCls
	/// </summary>
	internal class CapacityUseRatioItem
	{
		internal CapacityUseRatioItem(int workId, DateTime changeDate, decimal changeRatio)
		{
			this.WorkID = workId;
			this.ChangeDate = changeDate;
			this.ChangeRatio = changeRatio;
			this.InitialFreeRatio = 0M;
		}
		/// <summary>
		/// Vizualizace
		/// </summary>
		/// <returns></returns>
		public override string ToString()
		{
			return "{Time = " + this.ChangeDate.ToString("yyyy-MM-dd HH:mm") +
				(this.InvisibleItem ? " [Invisible]" : "") +
				(this.ChangeRatio > 0 ? "; Add: " : "; Down: ") + this.ChangeRatio.ToString() +
				"; Free = " + this.InitialFreeRatio.ToString() +
				(this.InvisibleItem ? "" : " => " + this.ResultingFreeRatio.ToString()) +
				" [WorkID=" + this.WorkID + "]}";
		}
		/// <summary>
		/// WorkID, který zapřičinil tuto změnu kapacity (přičemž počátek Work se pozná podle záporné hodnoty ChangeRatio, konec má ChangeRatio kladné)
		/// </summary>
		internal int WorkID { get; private set; }
		/// <summary>
		/// Datum a čas změny kapacity
		/// </summary>
		internal DateTime ChangeDate { get; private set; }
		/// <summary>
		/// Příznak systémové změny = když je JobID == 0, jde o začátek a konec kapacity. 
		/// Každý seznam vždy obsahuje dva tyto prvky 
		/// (na indexu 0 je systémový prvek s kladnou hodnotou ChangeRatio == +1M, poslední index obsahuje systémovou položku s hodnotou ChangeRatio == -1M).
		/// </summary>
		internal bool IsSystem { get { return (this.WorkID == 0); } }
		/// <summary>
		/// Stav volné kapacity na začátku této změny (před ní)
		/// </summary>
		internal decimal InitialFreeRatio { get; private set; }
		/// <summary>
		/// Kolik kapacity tato změna přidá (plus) nebo odebere (mínus)
		/// </summary>
		internal decimal ChangeRatio { get; private set; }
		/// <summary>
		/// Příznak neviditelnosti této položky.
		/// Pokud je neviditelná (true), pak nemá vliv na vývoj disponibilní kapacity, a procedury hledání volného času tuto položku ignorují.
		/// Více viz metody CapacityUseRatioMapCls._PrepareInterWork(), _FindFreeTimeToHistory(), _FindFreeTimeToFuture()
		/// </summary>
		internal bool InvisibleItem { get; set; }
		/// <summary>
		/// Stav volné kapacity po započtení této změny (po ní).
		/// Pokud je položka běžná (InvisibleItem == false), pak ResultingFreeRatio == InitialFreeRatio + ChangeRatio.
		/// Pokud je položka neviditelná (InvisibleItem == true), pak ResultingFreeRatio == InitialFreeRatio.
		/// </summary>
		internal decimal ResultingFreeRatio { get { return this.InitialFreeRatio + (this.InvisibleItem ? 0M : this.ChangeRatio); } }
		/// <summary>
		/// Přepočte průběžnou zásobu volné kapacity
		/// </summary>
		/// <param name="transientFreeRatio">Průběžná počáteční zásoba volné kapacity: na vstupu = počáteční, na výstupu výsledná.</param>
		/// <returns></returns>
		internal void RecalcItemFree(ref decimal transientFreeRatio)
		{
			this.InitialFreeRatio = transientFreeRatio;           // Vstupní hodnota (nastavím si ji do sebe)
			transientFreeRatio = this.ResultingFreeRatio;         // ResultingFreeRatio vrací (viz get) hodnotu Initial + Change, to vložíme do ref parametru pro další item.
		}
	}
	#endregion
	#region CLASS CapacityInterWorkCls : seznam položek WorkID, které mají být ignorovány při hledání volného času
	/// <summary>
	/// CapacityInterWorkCls : seznam položek WorkID, které mají být ignorovány při hledání volného času.
	/// Po vytvoření nového objektu dostane svoje ID, má prázdný seznam a není finalizován.
	/// Do tohoto objektu lze přidat položky (jednotlivě i hromadně), anebo je jednotlivé odebírat.
	/// Poté, kdy je objekt korektně naplněn, má být finalizován nastavením IsFinalized na true (zpátky to nejde!)
	/// Před finalizací nelze objekt do metod FindFreeTime posílat, jedině po nastavení finalizace.
	/// Po finalizaci do něj nelze přidávat.
	/// </summary>
	internal class CapacityInterWorkCls
	{
		public CapacityInterWorkCls()
		{
			this.InterWorkID = ++_LastID;
			this._InterWorkIdDict = new Dictionary<int, InterWorkConflictState>();
			this._IsFinalized = false;
		}
		/// <summary>
		/// Vizualizace
		/// </summary>
		/// <returns></returns>
		public override string ToString()
		{
			return "{InterWork: " + this._InterWorkIdDict.Count.ToString() + " položek, " + (this.IsFinalized ? "finalizovaný." : "otevřený.") + "}";
		}
		private static int _LastID;
		/// <summary>
		/// Příznak, že tento objekt nic neobsahuje.
		/// </summary>
		internal bool IsEmpty { get { return (this._InterWorkIdDict.Count == 0); } }
		/// <summary>
		/// ID tohoto seznamu, je autoinkrementální.
		/// </summary>
		internal int InterWorkID { get; private set; }
		/// <summary>
		/// <para>
		/// Příznak, že objekt je finalizován, zapečetěn. Do takového objektu už nejde přidávat ani odebírat další záznamy.
		/// Takový objekt může sloužit jako podklad do metod FindFreeTime, protože u něj je jistota, že jeho obsah bude vždy konstantní.
		/// Před finalizací nelze objekt do metod FindFreeTime posílat.
		/// </para>
		/// <para>
		/// Property lze setovat, ale pouze na hodnotu true.
		/// Po nastavení na true nelze měnit obsah, ale lze objekt používat jako parametr při volání metod FindFreeTime().
		/// </para>
		/// </summary>
		internal bool IsFinalized
		{
			get { return this._IsFinalized; }
			set
			{
				if (this._IsFinalized && !value)
					Throw.SysError(MessageInfo.Get("Objekt třídy CapacityInterWorkCls je finalizován, není možno jej odfinalizovat."));
				else if (this._IsFinalized != value)
					this._IsFinalized = value;
			}
		}
		private bool _IsFinalized;
		/// <summary>
		/// Seznam položek
		/// </summary>
		private Dictionary<int, InterWorkConflictState> _InterWorkIdDict;
		/// <summary>
		/// Metoda přidá celou řadu položek WorkID do tohoto seznamu.
		/// Pokud položka v seznamu už je, jen jí upraví stav.
		/// Metodu nelze volat za stavu, kdy je objekt již finalizován (pokus vyhodí chybu).
		/// </summary>
		/// <param name="workIDList">Soupis WorkID</param>
		/// <param name="state">Stav, který do položek chci vložit</param>
		internal void AddRangeWorkID(IEnumerable<int> workIDList, InterWorkConflictState state)
		{
			this._CheckNotFinalized();
			foreach (int workID in workIDList)
				this.AddWorkID(workID, state);
		}
		/// <summary>
		/// Metoda přidá danou položku WorkID do tohoto seznamu.
		/// Pokud položka v seznamu už je, jen jí upraví stav.
		/// Metodu nelze volat za stavu, kdy je objekt již finalizován (pokus vyhodí chybu).
		/// </summary>
		/// <param name="workID">ID jednotky práce (WorkUnit). Pokud se ptáme na položku 0, pak se vždy vrací false (položku 0 neobsahujeme).</param>
		/// <param name="state">Stav, který do položek chci vložit</param>
		internal void AddWorkID(int workID, InterWorkConflictState state)
		{
			this._CheckNotFinalized();
			if (!this._InterWorkIdDict.ContainsKey(workID))
				this._InterWorkIdDict.Add(workID, state);
			else
				this._InterWorkIdDict[workID] = state;
		}
		/// <summary>
		/// Metoda odebere danou položku WorkID z tohoto seznamu.
		/// Pokud položka v seznamu není, chybu to nevyvolá.
		/// Metodu nelze volat za stavu, kdy je objekt již finalizován (pokus vyhodí chybu).
		/// </summary>
		/// <param name="workID">ID jednotky práce (WorkUnit). Pokud se ptáme na položku 0, pak se vždy vrací false (položku 0 neobsahujeme).</param>
		internal void RemoveWorkID(int workID)
		{
			this._CheckNotFinalized();
			if (!this._InterWorkIdDict.ContainsKey(workID))
				this._InterWorkIdDict.Remove(workID);
		}
		/// <summary>
		/// Metoda zjistí, zda daná položka WorkID je obsažena v tomto seznamu.
		/// Vrací true = je zde / false = není zde.
		/// </summary>
		/// <param name="workID">ID jednotky práce (WorkUnit). Pokud se ptáme na položku 0, pak se vždy vrací false (položku 0 neobsahujeme).</param>
		/// <returns></returns>
		internal bool ContainWorkID(int workID)
		{
			return (workID != 0 && this._InterWorkIdDict.ContainsKey(workID));
		}
		/// <summary>
		/// Metoda zaregistruje stav konfliktu do dané položky.
		/// </summary>
		/// <param name="conflictWorkID"></param>
		internal void SetConflictState(int workID)
		{
			InterWorkConflictState conflictState;
			if (workID != 0 && this._InterWorkIdDict.TryGetValue(workID, out conflictState))
			{
				if (!(conflictState == InterWorkConflictState.NeedTransfer || conflictState == InterWorkConflictState.TransferFinished))
					this._InterWorkIdDict[workID] = InterWorkConflictState.NeedTransfer;
			}
		}
		/// <summary>
		/// Metoda ověří, že objekt dosud není finalizován.
		/// </summary>
		private void _CheckNotFinalized()
		{
			if (!this.IsFinalized) return;
			Throw.SysError(MessageInfo.Get("Objekt {CapacityInterWorkCls} je již finalizován, nelze jej modifikovat."));
		}
	}
	#endregion
	#region CLASS CapacityUseRatioFreeTime : informace o jednotce nalezeného volného času
	/// <summary>
	/// CapacityUseRatioFreeTime : informace o jednotce nalezeného volného času
	/// </summary>
	internal class CapacityUseRatioFreeTime
	{
		internal CapacityUseRatioFreeTime(CapacityDeskCls capacityDesk, int beginIndex, DateTime beginDate, int endIndex, DateTime endDate, decimal requestedFreeRatio, TimeRange.TimeDirection findTimeDir, LimitedCType unitLimitedCType)
		{
			this.CapacityDesk = capacityDesk;
			this.BeginIndex = beginIndex;
			this.EndIndex = endIndex;
			this.FreeTime = new TimeRange(beginDate, endDate);
			this.RequestedFreeRatio = requestedFreeRatio;
			this.FindTimeDir = findTimeDir;
			this.UnitLimitedCType = unitLimitedCType;
		}
		/// <summary>
		/// Vizualizace
		/// </summary>
		/// <returns></returns>
		public override string ToString()
		{
			return "{FreeTime: " + this.FreeTime.ToString() + "; Index Begin=" + this.BeginIndex + "; End=" + this.EndIndex + "}";
		}
		/// <summary> { get; }
		/// Index počátku nalezeného volného času. Za tento index bych mohl vložit počátek obsazení času.
		/// </summary>
		internal int BeginIndex { get; private set; }
		/// <summary> { get; }
		/// Index konce volného času. Před tento index bych mohl vložit konec obsazení času.
		/// </summary>
		internal int EndIndex { get; private set; }
		/// <summary> { get; }
		/// Časový interval volného času.
		/// </summary>
		internal TimeRange FreeTime { get; private set; }
		/// <summary> { get; }
		/// Požadované množství volného času, které tento záznam zaručuje
		/// </summary>
		internal decimal RequestedFreeRatio { get; private set; }
		/// <summary> { get; }
		/// Požadované množství volného času, které tento záznam zaručuje
		/// </summary>
		internal decimal FreeRatio { get { return RequestedFreeRatio;} }
		/// <summary> { get; }
		/// Směr, v němž jsme hledali volný čas. 
		/// Ovlivní úpravu objektu (InsertIndex) v případě, kdy se z tohoto objektu bude provádět registrace kapacity.
		/// </summary>
		internal TimeRange.TimeDirection FindTimeDir { get; private set; }
		/// <summary> { get; }
		/// Režim registrace kapacit (omezené / neomezené kapacity).
		/// Ovlivňuje řešení konfliktu nalezeného volného času s konfliktními položkami v seznamu ConflictWorkIdList.
		/// </summary>
		internal LimitedCType UnitLimitedCType { get; private set; }

		#region PROPERTY - PARENT OBJEKT A DALŠÍ PROPERTY PRO NADŘÍZENÝ SYSTÉM
		/// <summary>
		/// Data kapacitního stolu (Desk)
		/// </summary>
		internal CapacityDeskCls CapacityDesk { get; private set; }
		/// <summary> { get; set; }
		/// Klíč kapacitního zdroje, pro který je tento voný čas přidělen (asignován).
		/// Hodnotu lze setovat, ale to má svá pravidla.
		/// Vyvolá chybu při asignaci již asignovaného času.
		/// Nulu nelze vložit, obešla by se tím kontrola oprávnění.
		/// Pro uvolnění asignace je třeba volat metodu this.ClearAssignedCSource(int cSource), anebo this.ClearAssign().
		/// </summary>
		internal int AssignedToCSource { get; set; }
		/// <summary>
		/// Příznak, že tento volný čas má nějakou nevyčerpanou kapacitu.
		/// Po prvním vrácení z metody FindFreeTime ji má. 
		/// Ale poté, kdy je z tohoto volného času nějaká kapacita odčerpaná pro určitou práci, tuto volnou kapacitu mít může i nemusí.
		/// </summary>
		internal bool HasCapacity { get { return (this.FreeTime.Seconds > 0D); } }
		#endregion
		#region SADA PROPERTY NAČÍTANÝCH Z PARENT OBJEKTŮ, podle design doporučení "BĚLEHRAD" (nečíst data z hloubky konstrukcí, ale vytvořit na ně property)
		/// <summary>Klíč plánovací jednotky</summary>
		internal int PlanUnitC { get { return this.CapacityDesk.PlanUnitC; } }
		/// <summary>Číslo plánovacího stolu (výrobní linka v rámci jedné kapacitní jednotky), index base 0</summary>
		internal int DeskIndex { get { return this.CapacityDesk.DeskIndex; } }
		/// <summary>Stav kapacit, z něhož se zde nabízí čerpání</summary>
		internal CapacityLevelItemCls CapacityLevel { get { return this.CapacityDesk.CapacityLevel; } }
		#endregion
		#region METODY PRO PODPORU REGISTRACE KAPACIT
		/// <summary>
		/// Určí, zda tento volný čas je vyhovující pro požadované datum a směr hledání,
		/// se zohledněním požadavku na existující kapacitu.
		/// Vrací true = tento stav vyhovuje / false = nevyhovuje.
		/// </summary>
		/// <param name="findArgs">Parametry hledání (časový vektor, typ omezení kapacit). Pozor: typ omezení kapacit (LimitedCType) musí být již konkretizován!</param>
		/// <param name="findFreeRatio">Požadované (nejmenší) množství kapacity</param>
		/// <param name="withCapacity">Požadujeme stav kapacit s pozitivním zůstatkem kapacity. To není totéž, co parametr <see cref="limitedCType"/></param>
		/// <returns>true = tento stav vyhovuje / false = nevyhovuje</returns>
		internal bool IsSuitableForTime(CapacityFindFreeTimeArgs findArgs, decimal findFreeRatio, bool withCapacity)
		{
			return this._IsSuitableForTime(findArgs.TimeVector.Date, findArgs.TimeVector.Direction, this.UnitLimitedCType, findFreeRatio, withCapacity);
		}
		/// <summary>
		/// Určí, zda tento volný čas je vyhovující pro požadované datum a směr hledání,
		/// se zohledněním požadavku na existující kapacitu.
		/// Vrací true = tento stav vyhovuje / false = nevyhovuje.
		/// </summary>
		/// <param name="timeVector">Časový vektor hledání</param>
		/// <param name="limitedCType">Typ omezení kapacit. 
		/// Pokud je Unlimited, pak se chová jako by dosud žádná kapacita nebyla čerpaná. 
		/// Pokud je Limited, pak akceptuje hodnotu dosud vyčerpané kapacity. 
		/// Jiné hodnoty než Limited nebo Unlimited nejsou povoleny.</param>
		/// <param name="findFreeRatio">Požadované (nejmenší) množství kapacity</param>
		/// <param name="withCapacity">Požadujeme stav kapacit s pozitivním zůstatkem kapacity. To není totéž, co parametr <see cref="limitedCType"/></param>
		/// <returns>true = tento stav vyhovuje / false = nevyhovuje</returns>
		internal bool IsSuitableForTime(TimeVector timeVector, LimitedCType limitedCType, decimal findFreeRatio, bool withCapacity)
		{
			return this._IsSuitableForTime(timeVector.Date, timeVector.Direction, limitedCType, findFreeRatio, withCapacity);
		}
		/// <summary>
		/// Určí, zda tento volný čas je vyhovující pro požadované datum a směr hledání,
		/// se zohledněním požadavku na existující kapacitu.
		/// Vrací true = tento stav vyhovuje / false = nevyhovuje.
		/// </summary>
		/// <param name="date">Hledané datum</param>
		/// <param name="timeDir">Směr hledání</param>
		/// <param name="limitedCType">Typ omezení kapacit. 
		/// Pokud je Unlimited, pak se chová jako by dosud žádná kapacita nebyla čerpaná. 
		/// Pokud je Limited, pak akceptuje hodnotu dosud vyčerpané kapacity. 
		/// Jiné hodnoty než Limited nebo Unlimited nejsou povoleny.</param>
		/// <param name="withCapacity">Požadujeme stav kapacit s pozitivním zůstatkem kapacity. To není totéž, co parametr <see cref="limitedCType"/></param>
		/// <returns>true = tento stav vyhovuje / false = nevyhovuje</returns>
		internal bool IsSuitableForTime(DateTime date, TimeRange.TimeDirection timeDir, LimitedCType limitedCType, decimal findFreeRatio, bool withCapacity)
		{
			return this._IsSuitableForTime(date, timeDir, limitedCType, findFreeRatio, withCapacity);
		}
		/// <summary>
		/// Určí, zda tento volný čas je vyhovující pro požadované datum a směr hledání,
		/// se zohledněním požadavku na existující kapacitu.
		/// Vrací true = tento stav vyhovuje / false = nevyhovuje.
		/// </summary>
		/// <param name="date">Hledané datum</param>
		/// <param name="timeDir">Směr hledání</param>
		/// <param name="limitedCType">Typ omezení kapacit. 
		/// Pokud je Unlimited, pak se chová jako by dosud žádná kapacita nebyla čerpaná. 
		/// Pokud je Limited, pak akceptuje hodnotu dosud vyčerpané kapacity. 
		/// Jiné hodnoty než Limited nebo Unlimited nejsou povoleny.</param>
		/// <param name="withCapacity">Požadujeme stav kapacit s pozitivním zůstatkem kapacity. To není totéž, co parametr <see cref="limitedCType"/></param>
		/// <returns>true = tento stav vyhovuje / false = nevyhovuje</returns>
		private bool _IsSuitableForTime(DateTime date, TimeRange.TimeDirection timeDir, LimitedCType limitedCType, decimal findFreeRatio, bool withCapacity)
		{
			if (withCapacity && !this.HasCapacity) return false;           // Chci položku s jakoukoli kapacitou, a tady žádná není => konec.
			if (this.FreeRatio < findFreeRatio) return false;              // Nemám dostatečnou kapacitu => konec.
			switch (timeDir)
			{
				case TimeRange.TimeDirection.ToHistory:
					// Hledáme do minulosti, počínaje nejpozdějším stavem kapacit. 
					// Vyhovuje nám stav kapacit, který je v daném čase nebo před ním, nebo na hraně - podle požadavku na existující kapacitu:
					return (this.FreeTime.Begin < date || (!withCapacity && this.FreeTime.Begin == date));
				case TimeRange.TimeDirection.Stay:
					// Hledáme přesný stav k datu.
					// Vyhovuje nám stav kapacit, který začíná před/v daném čase a končí za/v daném čase, podle požadavku na existující kapacitu:
					return ((withCapacity && this.FreeTime.Begin < date && this.FreeTime.End > date) ||
						(!withCapacity && this.FreeTime.Begin <= date && this.FreeTime.End >= date));
				case TimeRange.TimeDirection.ToFuture:
					// Hledáme do budoucnosti, počínaje nejčasnějším stavem kapacit. 
					// Vyhovuje nám stav kapacit, který je v daném čase nebo po něm, nebo na hraně - podle požadavku na existující kapacitu:
					return (this.FreeTime.End > date || (!withCapacity && this.FreeTime.End == date));
			}
			return false;
		}
		/// <summary>
		/// Vrací true, pokud tento čas není možno použít pro daný kapacitní zdroj, protože čas už je přiřazen pro jiný kapacitní zdroj.
		/// </summary>
		/// <param name="cSource"></param>
		/// <returns></returns>
		internal bool NotForCSource(int cSource)
		{
			return (this.AssignedToCSource != 0 && this.AssignedToCSource != cSource);
		}
		/// <summary>
		/// Uvolní z asignace jakýkoli zdroj. Metoda je určená pro velký Clear asignací.
		/// Není vhodné ji volat kódu jednotlivého kapacitního zdroje, obchází se tak zabezpečení asignace.
		/// Z kapacitního zdroje je třeba volat metodu this.ClearAssignedCSource(int).
		/// </summary>
		internal void ClearAssign()
		{
			this.AssignedToCSource = 0;
		}
		/// <summary>
		/// Uvolní z asignace daný zdroj. 
		/// Pokud je tam jiný, vyhodí chybu. Pokud tam není asignovaný žádný zdroj, nevadí to.
		/// Metoda se volá typicky z kapacitního zdroje, když si uvolňuje svoje předchozí asignace.
		/// </summary>
		/// <param name="cSource"></param>
		internal void ClearAssignedCSource(int cSource)
		{
			if (this.AssignedToCSource == 0) return;
			if (this.AssignedToCSource == cSource)
				this.AssignedToCSource = 0;
			else
				Throw.SysError(MessageInfo.Get("Proběhl pokus o uvolnění asignace volného času, ale od nesprávného zdroje. Čas=%0, současná asignace=%1, žadatel o uvolnění=%2.", this.FreeTime, this.AssignedToCSource, cSource));
		}
		/// <summary>
		/// Do objektu vloží informaci, že je prázdný.
		/// </summary>
		internal void SetEmpty()
		{
			this.FreeTime = TimeRange.Empty;
		}
		/// <summary>
		/// Ořízne volný čas na tu část, která vyhovuje danému průběžnému času a směru.
		/// </summary>
		/// <param name="startTime">Čas řezu</param>
		/// <param name="timeDir">Směr, ve kterém leží ta část rozříznutého intervalu, kterou si chceme ponechat</param>
		internal void SplitTime(DateTime startTime, TimeRange.TimeDirection timeDir)
		{
			this.FreeTime = this.FreeTime.SplitUp(startTime, timeDir);
		}
		/// <summary>
		/// Ořízne volný čas na tu část, která vyhovuje danému průběžnému času a směru.
		/// </summary>
		/// <param name="timeVector">Ča a směr řezu</param>
		internal void SplitTime(TimeVector timeVector)
		{
			this.FreeTime = this.FreeTime.SplitUp(timeVector.Date, timeVector.Direction);
		}
		/// <summary>
		/// Změní data v objektu (FreeTime, JobAtBeginIndex a JobAtEndIndex) na základě vložení nového pracovního času WorkTime v daném směru.
		/// Směr alokace času je dán this.FindTimeDir: určuje prostor, v němž zůstává volné místo (ToFuture = za workTime, ToHistory = před workTime).
		/// </summary>
		/// <param name="workTime">Pracovní čas</param>
		internal void SetNewFreeTime(TimeRange workTime)
		{
			TimeRange freeTime = this.FreeTime;
			switch (this.FindTimeDir)
			{
				case TimeRange.TimeDirection.ToFuture:
					// Do volného času jsme vložili nový Job, a kapacity hledáme ToFuture.
					// Příští volný čas má být ten za nově vloženým Jobem:
					freeTime = new TimeRange(workTime.End, freeTime.End);
					break;
				case TimeRange.TimeDirection.ToHistory:
					// Do volného času jsme vložili nový Job, a kapacity hledáme ToHistory.
					// Příští volný čas má být ten před nově vloženým Jobem:
					freeTime = new TimeRange(freeTime.Begin, workTime.Begin);
					break;
			}
			this.FreeTime = freeTime;
		}
		/// <summary>
		/// Zaregistruje do daného volného času uvedenou práci.
		/// Registrace kapacit probíhá "Odspodu", tedy od nejnižší úrovně, která pak volá své nadřízené (Parent) za týmž účelem.
		/// </summary>
		/// <param name="workUnit">Specifikace práce (čas, úkol, průchod, fáze, koeficient)</param>
		/// <param name="interWork">Seznam položek WorkID pro ty WorkItem, které je možno přeregistrovat. Používá se při změně registrace, ne při nové registraci. Může být null.</param>
		internal void RegisterJob(IWorkUnitData workUnit, CapacityInterWorkCls interWork)
		{
			// Registrace kapacit probíhá "Odspodu", tedy od nejnižší úrovně, která pak volá své nadřízené (Parent) za týmž účelem:
			this.CapacityDesk.RegisterJob(this, workUnit, interWork);
		}
		#endregion

	}
	#endregion
	#region TESTY
	/// <summary>
	/// Testy systému CapacityUseRatioMapCls
	/// </summary>
	[Testable]
	public static class CapacityDeskFloatTest
	{
		/// <summary>
		/// Základní testy
		/// </summary>
		[TestPoint("Test funkcionality poměrného využití kapacit", TestScopeType.Standard)]
		public static void RunTest1()
		{
			_ErrMsg = "";

			CapacityUseRatioMapCls map = _GetStandardDesk();

			// Základní testy, stav kapacit beze změn a bez interWork:
			_TestFreeTimeLimited(map, 8D, TimeRange.TimeDirection.ToHistory, 0.25M, 4D, 8D);
			_TestFreeTimeLimited(map, 8D, TimeRange.TimeDirection.ToFuture, 0.25M, 8D, 11.5D);

			_TestFreeTimeLimited(map, 8D, TimeRange.TimeDirection.ToHistory, 0.15M, 2D, 8D);
			_TestFreeTimeLimited(map, 8D, TimeRange.TimeDirection.ToFuture, 0.15M, 8D, 12.0D);

			_TestFreeTimeLimited(map, 5D, TimeRange.TimeDirection.ToHistory, 0.60M, 0.5D, 1D);
			_TestFreeTimeLimited(map, 2D, TimeRange.TimeDirection.ToFuture, 0.60M, 5D, 11.5D);

			_TestFreeTimeLimited(map, 0D, TimeRange.TimeDirection.ToHistory, 0.60M, -1D, 1D);
			_TestFreeTimeLimited(map, 12D, TimeRange.TimeDirection.ToFuture, 0.60M, -1D, 11.5D);
			_TestFreeTimeLimited(map, -2D, TimeRange.TimeDirection.ToHistory, 0.60M, -1D, 1D);
			_TestFreeTimeLimited(map, 15D, TimeRange.TimeDirection.ToFuture, 0.60M, -1D, 11.5D);

			_TestFreeTimeLimited(map, 20D, TimeRange.TimeDirection.ToHistory, 0.40M, 4D, 11.5D);
			_TestFreeTimeLimited(map, -4D, TimeRange.TimeDirection.ToFuture, 0.60M, 0.5D, 1D);

			_TestFreeTimeLimited(map, 4D, TimeRange.TimeDirection.ToHistory, 0.40M, 0.5D, 1.0D);
			_TestFreeTimeLimited(map, 2D, TimeRange.TimeDirection.ToFuture, 0.40M, 4.0D, 11.5D);

			_TestFreeTimeLimited(map, 0.25D, TimeRange.TimeDirection.ToHistory, 0.40M, -1D, 0D);
			_TestFreeTimeLimited(map, 11.75D, TimeRange.TimeDirection.ToFuture, 0.40M, -1D, 0D);


			// Testy s interWork:
			CapacityInterWorkCls interWork = new CapacityInterWorkCls();
			interWork.AddWorkID(16, InterWorkConflictState.AllowMovement);    // Budeme ignorovat položku WorkID = 16, tj časy změn {3÷5}, 0.20
			interWork.IsFinalized = true;

			// S neviditelným úkolem:
			_TestFreeTimeLimitedInterWork(map, 4.5D, TimeRange.TimeDirection.ToHistory, 0.20M, 2.0D, 4.5D, interWork);
			// Bez něj (přepracuje se neviditelnost na "Vidím vše"):
			_TestFreeTimeLimitedInterWork(map, 4.5D, TimeRange.TimeDirection.ToHistory, 0.20M, 4.0D, 4.5D, null);

			// Původní neviditelný úkol (zapracuje se neviditelnost 1):
			_TestFreeTimeLimitedInterWork(map, 2.5D, TimeRange.TimeDirection.ToFuture, 0.80M, 5.0D, 11.5D, interWork);

			// Jiná kombinace InterWork (detekuje se změna sady neviditelnosti z 1 na 2):
			CapacityInterWorkCls interWork2 = new CapacityInterWorkCls();
			interWork2.AddWorkID(12, InterWorkConflictState.AllowMovement);    // Budeme ignorovat položku WorkID = 12, tj časy změn {2÷4}, 0.65
			interWork2.AddWorkID(14, InterWorkConflictState.AllowMovement);    // Budeme ignorovat položku WorkID = 12, tj časy změn {4÷5}, 0.30
			interWork2.IsFinalized = true;

			_TestFreeTimeLimitedInterWork(map, 2.25D, TimeRange.TimeDirection.ToFuture, 0.80M, 2.25D, 11.5D, interWork2);


			// Odeberu některé položky a ověřím výsledek:
			map.RemoveWork(12);
			_TestFreeTimeLimitedInterWork(map, 2.25D, TimeRange.TimeDirection.ToFuture, 0.80M, 2.25D, 4.0D, null);
			_TestFreeTimeLimitedInterWork(map, 2.25D, TimeRange.TimeDirection.ToFuture, 0.95M, 2.25D, 3.0D, null);

			// Zneviditelním všechny prvky 13,14,16,18 (ty, které jsou v čase po 4:00):
			CapacityInterWorkCls interWork3 = new CapacityInterWorkCls();
			interWork3.AddWorkID(13, InterWorkConflictState.AllowMovement);
			interWork3.AddWorkID(14, InterWorkConflictState.AllowMovement);
			interWork3.AddWorkID(16, InterWorkConflictState.AllowMovement);
			interWork3.AddWorkID(18, InterWorkConflictState.AllowMovement);
			interWork3.IsFinalized = true;
			_TestFreeTimeLimitedInterWork(map, 2.25D, TimeRange.TimeDirection.ToFuture, 0.95M, 2.25D, 12.0D, interWork3);
			_TestFreeTimeLimitedInterWork(map, 1.25D, TimeRange.TimeDirection.ToFuture, 0.95M, 2.00D, 12.0D, interWork3);
			_TestFreeTimeLimitedInterWork(map, 0.75D, TimeRange.TimeDirection.ToFuture, 0.95M, 0.75D, 1.0D, interWork3);
			_TestFreeTimeLimitedInterWork(map, 16D, TimeRange.TimeDirection.ToHistory, 0.95M, 2.00D, 12.0D, interWork3);


			// Testy neomezených kapacit A = s objektem po všech předchozích akcích:
			_TestFreeTimeUnLimited(map, 4.0D, TimeRange.TimeDirection.ToFuture, 0.75M, 4.0D, 12.0D);
			_TestFreeTimeUnLimited(map, 4.0D, TimeRange.TimeDirection.ToHistory, 0.75M, 0.0D, 4.0D);

			_TestFreeTimeUnLimited(map, 11.0D, TimeRange.TimeDirection.ToFuture, 1.75M, 11.0D, 12.0D);
			_TestFreeTimeUnLimited(map, 11.0D, TimeRange.TimeDirection.ToHistory, 1.75M, 0.0D, 11.0D);

			_TestFreeTimeUnLimited(map, 12.0D, TimeRange.TimeDirection.ToFuture, 1.00M, -1D, 0D);
			_TestFreeTimeUnLimited(map, 0.0D, TimeRange.TimeDirection.ToHistory, 1.00M, -1D, 0D);
			_TestFreeTimeUnLimited(map, 16.0D, TimeRange.TimeDirection.ToFuture, 1.00M, -1D, 0D);
			_TestFreeTimeUnLimited(map, -4.0D, TimeRange.TimeDirection.ToHistory, 1.00M, -1D, 0D);

			_TestFreeTimeUnLimited(map, 0.0D, TimeRange.TimeDirection.ToFuture, 1.00M, 0.0D, 12.0D);
			_TestFreeTimeUnLimited(map, 12.0D, TimeRange.TimeDirection.ToHistory, 1.00M, 0.0D, 12.0D);
			_TestFreeTimeUnLimited(map, -4.0D, TimeRange.TimeDirection.ToFuture, 1.00M, 0.0D, 12.0D);
			_TestFreeTimeUnLimited(map, 16.0D, TimeRange.TimeDirection.ToHistory, 1.00M, 0.0D, 12.0D);

			// Testy neomezených kapacit B = s novým objektem:
			map = _GetStandardDesk();
			_TestFreeTimeUnLimited(map, 4.0D, TimeRange.TimeDirection.ToFuture, 0.75M, 4.0D, 12.0D);
			_TestFreeTimeUnLimited(map, 4.0D, TimeRange.TimeDirection.ToHistory, 0.75M, 0.0D, 4.0D);

			_TestFreeTimeUnLimited(map, 11.0D, TimeRange.TimeDirection.ToFuture, 1.75M, 11.0D, 12.0D);
			_TestFreeTimeUnLimited(map, 11.0D, TimeRange.TimeDirection.ToHistory, 1.75M, 0.0D, 11.0D);

			_TestFreeTimeUnLimited(map, 12.0D, TimeRange.TimeDirection.ToFuture, 1.00M, -1D, 0D);
			_TestFreeTimeUnLimited(map, 0.0D, TimeRange.TimeDirection.ToHistory, 1.00M, -1D, 0D);
			_TestFreeTimeUnLimited(map, 16.0D, TimeRange.TimeDirection.ToFuture, 1.00M, -1D, 0D);
			_TestFreeTimeUnLimited(map, -4.0D, TimeRange.TimeDirection.ToHistory, 1.00M, -1D, 0D);

			_TestFreeTimeUnLimited(map, 0.0D, TimeRange.TimeDirection.ToFuture, 1.00M, 0.0D, 12.0D);
			_TestFreeTimeUnLimited(map, 12.0D, TimeRange.TimeDirection.ToHistory, 1.00M, 0.0D, 12.0D);
			_TestFreeTimeUnLimited(map, -4.0D, TimeRange.TimeDirection.ToFuture, 1.00M, 0.0D, 12.0D);
			_TestFreeTimeUnLimited(map, 16.0D, TimeRange.TimeDirection.ToHistory, 1.00M, 0.0D, 12.0D);

			// Hláška:
			string errMsg = _ErrMsg;
			Assert.ErrorIf(!String.IsNullOrEmpty(errMsg), errMsg);
		}
		#region PODPORA PRO TESTY
		/// <summary>
		/// Vrátí standardně připravený objekt s volnými časy podle testovacího grafikonu
		/// </summary>
		/// <returns></returns>
		private static CapacityUseRatioMapCls _GetStandardDesk()
		{
			DateTime begin = _Begin;
			CapacityUseRatioMapCls desk = CapacityUseRatioMapCls.CreateForTest(new TimeRange(begin, begin.AddHours(12)));
			desk.SaveUsedTime(12, new TimeRange(begin.AddHours(2), begin.AddHours(4)), 0.65M);
			desk.SaveUsedTime(13, new TimeRange(begin.AddHours(6), begin.AddHours(7)), 0.15M);
			desk.SaveUsedTime(14, new TimeRange(begin.AddHours(4), begin.AddHours(5)), 0.30M);
			desk.SaveUsedTime(15, new TimeRange(begin.AddHours(1), begin.AddHours(2)), 1.00M);
			desk.SaveUsedTime(16, new TimeRange(begin.AddHours(3), begin.AddHours(5)), 0.20M);
			desk.SaveUsedTime(17, new TimeRange(begin.AddHours(0), begin.AddMinutes(30)), 0.80M);
			desk.SaveUsedTime(18, new TimeRange(begin.AddHours(11.5), begin.AddHours(12)), 0.80M);
			return desk;
		}
		/// <summary>
		/// Zkusí najít volný čas, a ověří zda byl nalezen odpovídající.
		/// </summary>
		/// <param name="map">Mapa volných časů</param>
		/// <param name="findHour">Hledáme od hodiny...</param>
		/// <param name="timeDir">Hledáme ve směru</param>
		/// <param name="findRatio">Hledáme kapacitu</param>
		/// <param name="checkHourBegin">Očekáváme volný čas, Begin (-1 = null)</param>
		/// <param name="checkHourEnd">Očekáváme volný čas, End</param>
		private static void _TestFreeTimeLimited(CapacityUseRatioMapCls map, double findHour, TimeRange.TimeDirection timeDir, decimal findRatio, double checkHourBegin, double checkHourEnd)
		{
			DateTime startTime = _Begin.AddHours(findHour);
			CapacityFindFreeTimeArgs findArgs = new CapacityFindFreeTimeArgs(startTime, timeDir, LimitedCType.Limited);
			CapacityUseRatioFreeTime freeTime = map.FindFreeTime(findArgs, findRatio);
			_TestFreeTimeCheck(freeTime, checkHourBegin, checkHourEnd);
		}
		/// <summary>
		/// Zkusí najít volný čas, a ověří zda byl nalezen odpovídající.
		/// </summary>
		/// <param name="map">Mapa volných časů</param>
		/// <param name="findHour">Hledáme od hodiny...</param>
		/// <param name="timeDir">Hledáme ve směru</param>
		/// <param name="findRatio">Hledáme kapacitu</param>
		/// <param name="checkHourBegin">Očekáváme volný čas, Begin (-1 = null)</param>
		/// <param name="checkHourEnd">Očekáváme volný čas, End</param>
		/// <param name="interWork">Seznam neviditelných položek</param>
		private static void _TestFreeTimeLimitedInterWork(CapacityUseRatioMapCls map, double findHour, TimeRange.TimeDirection timeDir, decimal findRatio, double checkHourBegin, double checkHourEnd, CapacityInterWorkCls interWork)
		{
			DateTime startTime = _Begin.AddHours(findHour);
			CapacityFindFreeTimeArgs findArgs = new CapacityFindFreeTimeArgs(startTime, timeDir, LimitedCType.Limited);
			findArgs.InterWork = interWork;
			CapacityUseRatioFreeTime freeTime = map.FindFreeTime(findArgs, findRatio);
			_TestFreeTimeCheck(freeTime, checkHourBegin, checkHourEnd);
		}
		/// <summary>
		/// Zkusí najít volný čas, a ověří zda byl nalezen odpovídající.
		/// Neomezené kapacity.
		/// </summary>
		/// <param name="map">Mapa volných časů</param>
		/// <param name="findHour">Hledáme od hodiny...</param>
		/// <param name="timeDir">Hledáme ve směru</param>
		/// <param name="findRatio">Hledáme kapacitu</param>
		/// <param name="checkHourBegin">Očekáváme volný čas, Begin (-1 = null)</param>
		/// <param name="checkHourEnd">Očekáváme volný čas, End</param>
		private static void _TestFreeTimeUnLimited(CapacityUseRatioMapCls map, double findHour, TimeRange.TimeDirection timeDir, decimal findRatio, double checkHourBegin, double checkHourEnd)
		{
			DateTime startTime = _Begin.AddHours(findHour);
			CapacityFindFreeTimeArgs findArgs = new CapacityFindFreeTimeArgs(startTime, timeDir, LimitedCType.Unlimited);
			CapacityUseRatioFreeTime freeTime = map.FindFreeTime(findArgs, findRatio);
			_TestFreeTimeCheck(freeTime, checkHourBegin, checkHourEnd);
		}
		/// <summary>
		/// Ověří výsledek volného času
		/// </summary>
		/// <param name="freeTime"></param>
		/// <param name="checkHourBegin"></param>
		/// <param name="checkHourEnd"></param>
		private static void _TestFreeTimeCheck(CapacityUseRatioFreeTime freeTime, double checkHourBegin, double checkHourEnd)
		{
			DateTime checkBegin = _Begin.AddHours(checkHourBegin);
			DateTime checkEnd = _Begin.AddHours(checkHourEnd);
			TimeRange checkTime = new TimeRange(checkBegin, checkEnd);

			string error = null;
			if (checkHourBegin < 0D && freeTime != null)
				error = "Neočekávali jsme volný čas, ale nějaký se našel: " + freeTime.ToString();
			else if (checkHourBegin >= 0D && freeTime == null)
				error = "Očekávali jsme volný čas (" + checkTime.ToString() + "), ale žádný se nenašel.";
			else if (checkHourBegin >= 0D)
			{
				bool equalBegin = (freeTime.FreeTime.Begin == checkTime.Begin);
				bool equalEnd = (freeTime.FreeTime.End == checkTime.End);

				if (equalBegin && equalEnd)
				{ }
				else if (equalBegin && !equalEnd)
				{
					error = "Nesouhlasí čas konce volného času: je " + freeTime.FreeTime.ToString() + ", má být " + checkTime.ToString();
				}
				else if (!equalBegin && equalEnd)
				{
					error = "Nesouhlasí čas počátku volného času: je " + freeTime.FreeTime.ToString() + ", má být " + checkTime.ToString();
				}
				else if (!equalBegin && !equalEnd)
				{
					error = "Nesouhlasí čas počátku ani konce volného času: je " + freeTime.FreeTime.ToString() + ", má být " + checkTime.ToString();
				}
			}

			if (error != null)
				_ErrMsg += error + Environment.NewLine;
		}
		/// <summary>
		/// Konstantní datum 2010-10-01 00:00
		/// </summary>
		private static DateTime _Begin { get { return new DateTime(2010, 10, 1); } }
		private static string _ErrMsg;
		#endregion
	}
	#endregion
}
